package com.telecom.common.convention.result;

import com.github.pagehelper.PageInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 分页数据传输对象
 * 
 * <p>用于封装分页查询结果，支持从PageHelper的PageInfo对象或普通List转换。
 * 转换过程中会自动将DO/Entity/PO对象转换为VO对象。</p>
 * 
 * <p>该类实现了高性能的对象转换：
 * <ul>
 *   <li>使用缓存的转换器避免重复反射创建对象</li>
 *   <li>对大数据集使用并行流处理提高性能</li>
 *   <li>预分配集合容量避免扩容开销</li>
 * </ul>
 * </p>
 *
 * @param <V> 目标视图对象类型
 * @author telecom
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageDTO<V> {

    /**
     * 总记录数
     */
    private Long total;
    
    /**
     * 总页数
     */
    private Long pages;
    
    /**
     * 当前页数据列表
     */
    private List<V> list;
    
    /**
     * 用于缓存实例化的对象转换器，提高性能
     * 
     * <p>Key: 目标VO的Class对象</p>
     * <p>Value: 从源对象到目标对象的转换函数</p>
     */
    private static final Map<Class<?>, Function<Object, Object>> CONVERTOR_CACHE = new ConcurrentHashMap<>();
    
    /**
     * 数据量阈值，超过此值使用并行流处理
     * 
     * <p>当数据量大于此阈值时，会启用并行流来提高批量转换性能</p>
     */
    private static final int PARALLEL_THRESHOLD = 500;

    /**
     * 返回空分页结果
     * 
     * <p>当查询结果为空但仍需要分页信息时使用此方法。
     * 保留了总条数和总页数信息，但返回空列表。</p>
     * 
     * @param pageInfo PageHelper的分页结果
     * @param <V> 目标VO类型
     * @param <P> 原始PO类型
     * @return 包含分页信息但列表为空的分页对象
     */
    public static <V, P> PageDTO<V> empty(PageInfo<P> pageInfo){
        return new PageDTO<>(pageInfo.getTotal(), (long) pageInfo.getPages(), Collections.emptyList());
    }

    /**
     * 将PageHelper分页结果转为VO分页结果
     * 
     * <p>此方法用于将MyBatis PageHelper的分页查询结果转换为前端可用的VO对象分页结果。
     * 会自动处理对象之间的属性拷贝，通过反射将相同属性名的值复制到目标VO对象中。</p>
     * 
     * <p>性能优化：
     * <ul>
     *   <li>空结果直接返回，避免不必要的转换</li>
     *   <li>使用缓存的转换器，避免重复创建转换函数</li>
     *   <li>根据数据量自动选择串行或并行处理</li>
     * </ul>
     * </p>
     * 
     * @param pageInfo PageHelper的分页结果
     * @param voClass 目标VO类型的Class对象
     * @param <V> 目标VO类型
     * @param <P> 原始PO/Entity类型
     * @return 转换后的VO分页对象
     */
    public static <V, P> PageDTO<V> of(PageInfo<P> pageInfo, Class<V> voClass) {
        // 1.非空校验
        List<P> records = pageInfo.getList();
        if (records == null || records.isEmpty()) {
            // 无数据，返回空结果
            return empty(pageInfo);
        }
        // 2.数据转换
        List<V> vos = copyToList(records, voClass);
        // 3.封装返回
        return new PageDTO<>(pageInfo.getTotal(), (long) pageInfo.getPages(), vos);
    }

    /**
     * 将普通List转为VO分页结果
     * 
     * <p>此方法适用于已有普通列表数据，但需要将其转换为分页格式的场景。
     * 比如从缓存获取的数据或者非PageHelper查询的结果。</p>
     * 
     * <p>与PageHelper版本的区别在于这里需要手动提供总记录数和总页数，而不是自动从PageInfo中获取。
     * 对象转换逻辑与PageHelper版本相同，都会自动完成对象属性的复制。</p>
     * 
     * @param records 原始数据列表
     * @param total 总记录数
     * @param pages 总页数
     * @param voClass 目标VO类型的Class对象
     * @param <V> 目标VO类型
     * @param <P> 原始PO/Entity类型
     * @return 转换后的VO分页对象
     */
    public static <V, P> PageDTO<V> of(List<P> records, long total, long pages, Class<V> voClass) {
        // 1.非空校验
        if (records == null || records.isEmpty()) {
            // 无数据，返回空结果
            return new PageDTO<>(total, pages, Collections.emptyList());
        }
        // 2.数据转换
        List<V> vos = copyToList(records, voClass);
        // 3.封装返回
        return new PageDTO<>(total, pages, vos);
    }



    /**
     * 将PO/Entity列表拷贝为VO列表
     * 
     * <p>此方法实现了高效的对象批量转换，主要特点：</p>
     * <ol>
     *   <li>使用缓存的转换函数，避免重复创建反射操作</li>
     *   <li>根据列表大小自动选择串行或并行处理模式</li>
     *   <li>预先设置集合容量，避免动态扩容带来的性能损失</li>
     *   <li>小数据量使用串行流，大数据量自动切换为并行流处理</li>
     * </ol>
     * 
     * <p>注意：此转换假设VO对象有无参构造函数，且字段名与PO对象相匹配</p>
     * 
     * @param records 原始PO/Entity列表
     * @param voClass VO类的Class对象
     * @param <V> 目标VO类型
     * @param <P> 原始PO/Entity类型
     * @return 转换后的VO列表
     */
    @SuppressWarnings("unchecked")
    private static <V, P> List<V> copyToList(List<P> records, Class<V> voClass) {
        int size = records.size();
        if (size == 0) {
            return Collections.emptyList();
        }
        
        // 获取或创建转换器 - 从缓存中获取，如不存在则创建并放入缓存
        Function<P, V> convertor = (Function<P, V>) CONVERTOR_CACHE.computeIfAbsent(
            voClass,
            clazz -> (Function<Object, Object>) source -> {
                // 使用Spring BeanUtils通过反射创建目标类实例
                Object target = BeanUtils.instantiateClass((Class<?>) clazz);
                // 将source对象中的属性值拷贝到target对象中（相同属性名）
                BeanUtils.copyProperties(source, target);
                return target;
            }
        );
        
        // 根据数据量选择处理方式
        if (size <= PARALLEL_THRESHOLD) {
            // 数据量较小时使用普通顺序流处理，避免并行处理的额外开销
            return records.stream()
                    .map(convertor)  // 将每个PO对象转换为VO对象
                    .collect(Collectors.toCollection(() -> new ArrayList<>(size))); // 预分配容量，避免扩容
        } else {
            // 数据量较大时使用并行流提高性能
            return records.parallelStream()
                    .map(convertor)  // 并行将PO对象转换为VO对象
                    .collect(Collectors.toCollection(() -> new ArrayList<>(size))); // 预分配容量，避免扩容
    }
    }
}